package pro.cherish.smartfile.component;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import pro.cherish.smartfile.common.exception.BusinessException;
import pro.cherish.smartfile.config.AppContext;
import pro.cherish.smartfile.config.properties.SysProperties;
import pro.cherish.smartfile.module.dto.TaskDto;

import javax.annotation.Resource;
import java.io.*;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 创建人：lys
 * @version 版本号：V1.0
 * <p>
 * ***************************修订记录************************************
 * 2020-12-03 lys 创建该类功能。
 * **********************************************************************
 * </p>
 * @ClassName 类名：FileHelper
 * @Description 功能说明：
 * <p>
 *
 * </p>
 * **********************************************************************
 * @date 创建日期： 2020-12-03 lys
 */
@Slf4j
public class FileHelper {

    private static FileHelper fileHelper;

    @Resource
    private SysProperties sysProperties;

    public FileHelper() {
        AppContext.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(this);
    }

    public synchronized static FileHelper getInstance() {
        if (FileHelper.fileHelper == null) {
            return new FileHelper();
        } else {
            return FileHelper.fileHelper;
        }
    }

    /**
     * 合并一个文件
     *
     * @param taskDto
     * @return
     */
    public File merge(TaskDto taskDto) {
        // 成功上传完
        File archiveDir = sysProperties.getArchivePath(taskDto.getUploadTime(), taskDto.getId());
        File tempDir = sysProperties.getTempRootPath(taskDto.getId());
        if (!FileUtil.exist(tempDir)) {
            throw new BusinessException("文件不存在");
        }
        File[] tempFiles = getExistChunks(taskDto.getId());
        String archiveFilePath = archiveDir + File.separator + taskDto.getFilename();
        File archiveFile;
        try {
            archiveFile = FileUtil.touch(archiveFilePath);
        } catch (IORuntimeException e) {
            log.error("文件[{}]创建失败,错误信息={}", archiveFilePath, e.toString());
            throw new BusinessException("文件合并失败");
        }
        FileOutputStream outputStream = null;
        FileChannel outChannel = null;
        try {
            List<File> collect = Arrays.stream(tempFiles).sorted((o1, o2) -> {
                String[] split = o1.toPath().getFileName().toString().split("\\.");
                int i1 = Integer.parseInt(split[0]);
                String[] split2 = o2.toPath().getFileName().toString().split("\\.");
                int i2 = Integer.parseInt(split2[0]);
                return i1 - i2;
            }).collect(Collectors.toList());
            outputStream = new FileOutputStream(archiveFile);
            outChannel = outputStream.getChannel();
            for (File file : collect) {
                try {
                    FileChannel fileChannel = new FileInputStream(file).getChannel();
                    int position = 0;
                    long size = fileChannel.size();
                    while (position < size) {
                        // 规避单次"2G"问题
                        long count = fileChannel.transferTo(position, size, outChannel);
                        if (count > 0) {
                            position += count;
                            size -= count;
                        }
                    }
                    fileChannel.close();
                } catch (IOException e) {
                    throw new BusinessException(StrUtil.format("临时文件[{}]加入合并出现问题", file.toPath()));
                }
            }

        } catch (Exception e) {
            log.error(e.toString());
        } finally {
            try {
                outChannel.close();
                outputStream.close();
            } catch (Exception e) {
                log.error(e.toString());
            }
        }
        return archiveFile;
    }


    /**
     * 本地临时文件清除
     *
     * @param identify
     */
    public void clearTemp(String identify) {
        File tempDir = sysProperties.getTempRootPath(identify);
        FileUtil.del(tempDir);
    }

    /**
     * 本地归档文件清除
     *
     * @param uploadTime
     * @param identify
     */
    public void clearArchive(Date uploadTime, String identify) {
        File archiveFile = sysProperties.getArchivePath(uploadTime, identify);
        FileUtil.del(archiveFile);
    }

    /**
     * 归档文件是否存在
     *
     * @param uploadDate
     * @param taskId
     * @return
     */
    public Boolean existArchive(Date uploadDate, String taskId) {
        File archiveFile = sysProperties.getArchivePath(uploadDate, taskId);
        return FileUtil.exist(archiveFile);
    }

    /**
     * 分块文件是否存在
     *
     * @param uploadTime
     * @param taskId
     * @return
     */
    public Boolean existChunk(Date uploadTime, String taskId, int chunk) {
        return FileUtil.exist(sysProperties.getTempRootPath(taskId) + File.separator + ChunkUtil.buildChunkTempFileName(chunk));
    }

    /**
     * 获取存在的chunk本地临时文件
     *
     * @param taskId
     * @return
     */
    public File[] getExistChunks(String taskId) {
        File[] tempDirFiles = FileUtil.ls(sysProperties.getTempRootPath(taskId).toString());
        return Arrays.stream(tempDirFiles).filter(item -> !item.getName().endsWith("txt")).toArray(File[]::new);
    }

    /**
     * 清除所有的相关文件
     *
     * @param uploadTime
     * @param identify
     */
    public void clearAllAboutFile(Date uploadTime, String identify) {
        // 1)清除临时文件
        clearTemp(identify);
        // 2)清除归档文件
        clearArchive(uploadTime, identify);
    }

    public void writeMd5File(String currentChunkMd5, String filePath) {
        FileWriter writer;
        try {
            writer = new FileWriter(filePath, false);
            writer.write(currentChunkMd5);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            log.error(e.toString());
        }
    }
}
